from lagrange import *

bat_dual_rows = []
#for i in bat_dual_row_names:
#do subgradient for each case

iter = 0
N = 20
b = 100
#t = 1
#report = True

#rho = 0.8
eps = 1

def subgrad(prob, dual_rows):

    A, b, sen, c = initlr(prob, dual_rows)

    # set initial value of l
    l = 0 * np.ones( len(dual_rows) )

    z = 0
    z_opt = 1000000
    iter = 0
    iter_opt_cnt = 0
    iter_opt = 0
    rho = 0.9
    t = 1
    status = ""
    
    l_pos_ind = []
    l_eql_ind = []
    l_neg_ind = []

    for i in range( len(sen) ):
        if sen[i] == "E":
            l_eql_ind.append(i)
        elif sen[i] == "L":
            l_pos_ind.append(i)
        else:
            l_neg_ind.append(i)

    while True:
        if iter > 1000:
            status = "U"
            # print "Reach iteration limit"
            break
        
        if iter_opt_cnt > 40:
            status = "L"            
            # print "Reach optimal iteration limit"
            break

        solvelr(prob, l, A, b, c)
        x = np.array( prob.solution.get_values() )
        z = prob.solution.get_objective_value()
        g = gap(x, A, b)

        if opt_test(x, l, A, b, sen):
            z_opt = np.dot(x, c)
            # print "Reach optimal solution"
            # z_opt = z
            iter_opt = iter
            status = "O"

            break
        
        temp_l = l - t*g
        if len(l_pos_ind) != 0:
            l[l_pos_ind] = np.maximum( np.zeros(len(l_pos_ind) ), temp_l[l_pos_ind])
        if len(l_eql_ind) != 0:
            # l[l_eql_ind] = np.maximum( np.zeros(len(l_eql_ind) ), temp_l[l_eql_ind])
            l[l_eql_ind] = temp_l[l_eql_ind]
        if len(l_neg_ind) != 0:
            l[l_neg_ind] = np.minimum( np.zeros(len(l_neg_ind) ), temp_l[l_neg_ind])

        t = t * rho
        #t = 1 / (iter + 1.0)
        if z < z_opt:
            z_opt = z
            iter_opt = iter
            iter_opt_cnt = 1
        elif z == z_opt:
            iter_opt = iter
            iter_opt_cnt = iter_opt_cnt + 1
        else:
            iter_opt_cnt = iter_opt_cnt + 1

        iter = iter+1
        
    return z_opt, iter_opt, iter, status
# the outer loop should iterate through all dual rows
# for dual_rows in dual_rows_list

def ipsolve(prob):
    prob.solve()
    ip_opt_val = prob.solution.get_objective_value()
    prob.set_problem_type(type=0)
    prob.solve()
    #prob.parameters.output.writelevel.set(0)

    lp_opt_val = prob.solution.get_objective_value()
    return ip_opt_val, lp_opt_val

#dual_rows = ["u" + str(i+1) for i in range(N)] 
dual_rows = ["u" + str(i+1) for i in range(N)] + ["v" + str(i+1) for i in range(N)]
#dual_rows = ["v" + str(i+1) for i in range(N)]
#dual_rows = ["u" + str(i+1) for i in range(N)] + ["k"]

prob = cplex.Cplex()
print "%10s" % "case_num",
print "%10s" % "iter_total",
print "%10s" % "iter_opt",
print "%6s"  %  "status",
print "%12s" %  "ld_opt",
print "%12s" %  "ip_opt",
print "%12s" %  "lp_opt",
print "%12s" % "opt_gap"

for case in range(10):
    seed = (case+1) * 1000
    prob = initip(seed, N, b)
    prob_org = initip(seed, N, b)

    ld_opt_val, ld_opt_iter, tt_iter, status = subgrad(prob, dual_rows)
    ip_opt_val, lp_opt_val  = ipsolve(prob_org)

    opt_gap = abs(ld_opt_val - ip_opt_val)/ ip_opt_val

    print "%10d"    % (case+1),
    print "%10d"    % tt_iter,
    print "%10d"    % ld_opt_iter,
    print "%6s"     % status,
    print "%12.6f"  % ld_opt_val,
    print "%12.6f"  % ip_opt_val,
    print "%12.6f"  % lp_opt_val,
    print "%12.6f"  % opt_gap
# Then print out
    
